defmodule Iocp.Server do
  @moduledoc """
  TCP server that accepts connections from IOCP devices
  """

  use GenServer

  def start_link(port) do
    GenServer.start_link(__MODULE__, port, name: __MODULE__)
  end

  @impl true
  def init(port) do
    {:ok, tcp_server_socket} = :gen_tcp.listen(port, [:list, packet: :raw, active: false, reuseaddr: true])
    GenServer.cast(__MODULE__, :accept)
    {:ok, tcp_server_socket}
  end

  @impl true
  def handle_cast(:accept, tcp_server_socket) do
    {:ok, client} = :gen_tcp.accept(tcp_server_socket)
    {:ok, device_address} = get_client_device_address(client)
    Iocp.Worker.Supervisor.start_child(client, device_address)
    GenServer.cast(__MODULE__, :accept)
    {:noreply, tcp_server_socket}
  end

  defp get_client_device_address(client) do
    with {:ok, {ip, _port}} <- :inet.peername(client),
         {:ok, mac} <- arp(ip),
         {:ok, device_address} <- Iocp.AddressLookup.device_address_for(mac)
    do
      {:ok, device_address}
    else
      error -> error
    end
  end

  @mac_regex ~r/\b[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}\b/
  defp arp({a, b, c, d}) do
    ip = [a, b, c, d] |> Enum.join(".")
    {stdout, 0} = System.cmd("arp", ["-n", ip])
    case Regex.run(@mac_regex, stdout) do
      nil -> :error
      [mac] -> {:ok, mac}
    end
  end
end
